package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"webook/internal/domain"
)

type WeChatService interface {
	AuthURL(ctx context.Context, state string) (string, error)
	VerifyCode(ctx context.Context, code string, state string) (domain.WeChatInfo, error)
}

var redirectURI = url.PathEscape("https://meoying.com/oauth2/wechat/callback")

type WechatServiceImpl struct {
	appID  string
	secret string
	client *http.Client
}

func NewWeChatService(appID, secret string) WeChatService {
	return &WechatServiceImpl{
		appID:  appID,
		secret: secret,
		client: http.DefaultClient,
	}
}

func (svc *WechatServiceImpl) AuthURL(ctx context.Context, state string) (string, error) {
	urlPattern := "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect"
	return fmt.Sprintf(urlPattern, svc.appID, redirectURI, state), nil
}

func (svc *WechatServiceImpl) VerifyCode(ctx context.Context, code string, state string) (domain.WeChatInfo, error) {
	// 1. 构造请求url
	accessURL := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", svc.appID, svc.secret, code)
	// 2. 发起HTTP请求
	req, err := http.NewRequest(http.MethodGet, accessURL, nil)
	if err != nil {
		return domain.WeChatInfo{}, err
	}
	resp, err := svc.client.Do(req)
	if err != nil {
		return domain.WeChatInfo{}, err
	}
	// 3. 读取响应
	var res Result
	err = json.NewDecoder(resp.Body).Decode(&res)
	if err != nil {
		return domain.WeChatInfo{}, err
	}
	if res.ErrCode != 0 {
		// 调用微信接口失败
		return domain.WeChatInfo{}, errors.New(res.ErrMsg)
	}
	return domain.WeChatInfo{
		OpenId:  res.OpenId,
		UnionId: res.UnionId,
	}, nil
}

type Result struct {
	AccessToken string `json:"access_token"`
	// access_token接口调用凭证超时时间，单位（秒）
	ExpiresIn int64 `json:"expires_in"`
	// 用户刷新access_token
	RefreshToken string `json:"refresh_token"`
	// 授权用户唯一标识
	OpenId string `json:"openid"`
	// 用户授权的作用域，使用逗号（,）分隔
	Scope string `json:"scope"`
	// 当且仅当该网站应用已获得该用户的userinfo授权时，才会出现该字段。
	UnionId string `json:"unionid"`

	// 错误返回
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}
